const path = require('node:path')
const fse = require('fs-extra')
const glob = require('fast-glob')

const { writeFile, convertToCjs, logError } = require('./build.utils')

const root = path.resolve(__dirname, '..')
function resolve (_path) {
  return path.resolve(root, _path)
}

const cjsBanner = setName => `/**
 * DO NOT EDIT THIS FILE. It is automatically generated
 * from its .mjs counterpart (same filename but with .mjs extension).
 * Edit that file instead (${ setName }).
 */
`

const svgIconSetBanner = setName => `
/*
 * DO NOT EDIT THIS FILE. It is automatically generated
 * from its webfont counterpart (same filename without "svg-" prefix).
 * Edit that file instead (${ setName }.mjs).
 */`

// generic conversion
const convert = str => str.replace(/(-\w)/g, m => m[ 1 ].toUpperCase())
const materialConvert = (str, old, prefix) => {
  if (old !== '') {
    str = str.substring(old.length)
  }
  return (prefix + str).replace(/(_\w)/g, m => m[ 1 ].toUpperCase())
}

const iconTypes = [
  {
    name: 'material-icons-outlined',
    regex: /^o_/,
    convert: str => materialConvert(str, 'o_', 'outlined_')
  },
  {
    name: 'material-icons-round',
    regex: /^r_/,
    convert: str => materialConvert(str, 'r_', 'round_')
  },
  {
    name: 'material-icons-sharp',
    regex: /^s_/,
    convert: str => materialConvert(str, 's_', 'sharp_')
  },
  {
    name: 'material-symbols-outlined',
    regex: /^sym_o_/,
    convert: str => materialConvert(str, 'sym_o_', 'sym_outlined_')
  },
  {
    name: 'material-symbols-rounded',
    regex: /^sym_r_/,
    convert: str => materialConvert(str, 'sym_r_', 'sym_rounded_')
  },
  {
    name: 'material-symbols-sharp',
    regex: /^sym_s_/,
    convert: str => materialConvert(str, 'sym_s_', 'sym_sharp_')
  },
  {
    name: 'mdi-v6',
    regex: /^mdi-/,
    convert
  },
  {
    name: 'mdi-v7',
    regex: /^mdi-/,
    convert
  },
  {
    name: 'ionicons-v4', // last web font version
    regex: /^ion-/,
    convert: str => convert(
      /ion-(md|ios)-/.test(str) === true
        ? str
        : str.replace(/ion-/, 'ion-md-')
    )
  },
  {
    name: 'fontawesome-v6',
    regex: /^fa[brs] fa-/,
    convert: str => convert(str.replace(' fa-', '-'))
  },
  {
    name: 'eva-icons',
    regex: /^eva-/,
    convert
  },
  {
    name: 'themify',
    regex: /^ti-/,
    convert
  },
  {
    name: 'line-awesome',
    regex: /^la[brs] la-/,
    convert: str => convert(
      (str.startsWith('las la-') === true ? str + '-solid' : str)
        .replace(/^la[brs] la-/, 'la-')
    )
  },
  {
    name: 'bootstrap-icons',
    regex: /^bi-/,
    convert
  },
  // must be last as it's a catch-all
  {
    name: 'material-icons',
    regex: /./,
    convert: str => materialConvert(str, '', 'mat_')
  }
]

function convertWebfont (name, originalType) {
  const type = originalType.regex.test(name)
    ? originalType
    : iconTypes.find(type => type.regex.test(name)) || iconTypes[ 0 ]

  return {
    importName: type.name,
    variableName: type.convert(name)
  }
}

function toObject (arr) {
  const obj = {}
  arr.forEach(item => {
    obj[ item ] = []
  })
  return obj
}

const iconNames = iconTypes.map(type => type.name)

const splitDelimiter = 'export default {'

function splitContent (str) {
  const content = str.split(splitDelimiter)

  return {
    outsideOfExport: content[ 0 ],
    insideOfExport: splitDelimiter + content[ 1 ]
  }
}

function generateSvgFile (type) {
  const original = fse.readFileSync(resolve(`icon-set/${ type.name }.mjs`), 'utf-8')
  const { outsideOfExport, insideOfExport } = splitContent(original)

  const importList = toObject(iconNames)

  const contentString = insideOfExport
    .replace(/name: '(.+)'/, 'name: ""')
    .replace(/'(.+)'/g, (_match, name) => {
      const { importName, variableName } = convertWebfont(name, type)
      if (!importList[ importName ].includes(variableName)) {
        importList[ importName ].push(variableName)
      }
      return variableName
    })
    .replace(/name: ""/, `name: 'svg-${ type.name }'`)

  const importString = Object.keys(importList)
    .filter(listName => importList[ listName ].length > 0)
    .map(listName => 'import {\n  ' + importList[ listName ].join(',\n  ') + `\n} from '@quasar/extras/${ listName }'`)
    .join('\n\n')

  const content = [
    svgIconSetBanner(type.name),
    importString,
    outsideOfExport,
    contentString
  ].filter(str => str).join('\n\n')

  const iconFile = resolve(`icon-set/svg-${ type.name }.mjs`)

  let oldContent = ''

  try {
    oldContent = fse.readFileSync(iconFile, 'utf-8')
  }
  catch (e) {}

  return content.split(/[\n\r]+/).join('\n') !== oldContent.split(/[\n\r]+/).join('\n')
    ? writeFile(iconFile, content)
    : Promise.resolve()
}

function generateCjsCounterparts () {
  const promises = []
  try {
    glob.sync(resolve('icon-set/*.mjs'), { cwd: root, absolute: true })
      .forEach(file => {
        const content = fse.readFileSync(file, 'utf-8')
        const cjsFile = file.replace('.mjs', '.js')
        const banner = file.indexOf('svg') !== -1
          ? ''
          : cjsBanner(path.basename(file))

        promises.push(
          writeFile(cjsFile, convertToCjs(content, banner))
        )
      })

    return Promise.all(promises)
  }
  catch (err) {
    logError('build.icon-sets.js: something went wrong...')
    console.log()
    console.error(err)
    console.log()
    process.exit(1)
  }
}

module.exports.generate = async function () {
  await Promise.all(
    iconTypes.map(generateSvgFile)
  )

  await generateCjsCounterparts()
}
